﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;

namespace server.app
{
    using TcpMessageFuncDic = Dictionary<comm.objst.TcpMessageType, Action<comm.TcpClient, comm.objst.TcpMessage>>;

    //#region Player
    public sealed class Player : comm.world.BObject
    {
        public static string ObjectIdToPlayerId(string objectId)
        {
            var ss = objectId.Split(':');
            if (ss.Length == 2)
            {
                return ss[0];
            }
            return null;
        }

        public lib.Hero HeroObject
        {
            get
            {
                var obj = _world.ObjectManager.GetObject(ObjectId);
                if (obj is lib.Hero)
                {
                    return (lib.Hero)obj;
                }
                return null;
            }
        }
        public string UserId { get { return _tablePlayer.UserId; } }
        public string PlayerId { get { return _tablePlayer.PlayerId; } }
        public string Nickname { get { return _tablePlayer.Nickname; } }
        public string ClientId { get; private set; }
        public string ObjectId { get; private set; }

        List<Object> _objList = new List<Object>();
        comm.UniqueID _uid = null;
        World _world = null;
        comm.objst.TablePlayer _tablePlayer = null;

        public Player(World world, string clientId, comm.objst.TablePlayer tablePlayer)
        {
            _tablePlayer = tablePlayer;
            ClientId = clientId;
            ObjectId = _tablePlayer.PlayerId + ":hero";

            _world = world;
            _uid = new comm.UniqueID(_tablePlayer.PlayerId + ":");
        }

        public string NewObjectId()
        {
            return _uid.NewId();
        }
        public void Send(comm.objst.TcpMessageType type, object obj, string msgId = null, long timeOffset = 0)
        {
            _world.PlayerManager.SendToPlayer(PlayerId, type, obj, msgId, timeOffset);
        }
        public void Send(byte[] data)
        {
            _world.PlayerManager.SendToPlayer(PlayerId, data);
        }
        public void Save()
        {
            if (HeroObject != null)
            {
                _tablePlayer.NValueDic = HeroObject.NValueManager.GetAllNVValue(true);
                _tablePlayer.StateList = new List<comm.objst.State>(HeroObject.StateManager.GetAllStateInfo(true));

                Console.WriteLine("SavePlayer/ nvvalue:" + comm.JsonSerializer.Serialize(_tablePlayer.NValueDic) + " state:" + comm.JsonSerializer.Serialize(_tablePlayer.StateList));

                _world.HeroManager.UpdateHero(_tablePlayer);
            }
        }
        public comm.objst.HeroInfo GetHeroInfo()
        {
            return new comm.objst.HeroInfo
            {
                UserId = UserId,
                Nickname = Nickname,
                PlayerId = PlayerId,
                DObjectId = HeroObject.DObjectId,
                NVValueDic = HeroObject.NValueManager.GetAllNVValue(null, comm.objst.NValueBehaviorType.HERO_OTHRE_PLAYER_VIEW)
            };
        }
        public comm.objst.HeroFullInfo GetHeroFullInfo()
        {
            return new comm.objst.HeroFullInfo
            {
                Nickname = Nickname,
                UserId = UserId,
                PlayerId = PlayerId,
                DObjectId = HeroObject.DObjectId,
                NVValueDic = HeroObject.NValueManager.GetAllNVValue()
            };
        }
        public comm.objst.EquipInfo[] GetAllEquipInfo()
        {
            var list = new List<comm.objst.EquipInfo>();
            var itemDic = _world.ItemManager.GetAllItemDic(_tablePlayer.EquipDic.ToAllItemId());
            foreach (var equipType in _tablePlayer.EquipDic.Keys)
            {
                var itemId = _tablePlayer.EquipDic[equipType];
                list.Add(new comm.objst.EquipInfo
                {
                    EquipType = equipType,
                    Item = itemDic.ContainsKey(itemId) ? new comm.objst.ItemInfo(itemDic[itemId]) : null
                });
            }
            return list.ToArray();
        }
        public comm.objst.BackpackInfo[] GetAllBackpackInfo()
        {
            return _world.BackpackManager.GetAllBackpack(PlayerId);
        }
        public comm.objst.UnitReadyInfo[] GetAllUnitReadyInfo()
        {
            var list = new List<comm.objst.UnitReadyInfo>();
            var unitDic = _world.UnitManager.GetAllUnitDic(_tablePlayer.UnitReadyDic.ToAllUnitId());
            foreach (var pos in _tablePlayer.UnitReadyDic.Keys)
            {
                var item = _tablePlayer.UnitReadyDic[pos];
                if (unitDic.ContainsKey(item.UintId))
                {
                    list.Add(new comm.objst.UnitReadyInfo
                    {
                        Pos = pos,
                        Unit = new comm.objst.UnitInfo(unitDic[item.UintId])
                    });
                }
            }
            return list.ToArray();
        }

        protected override void OnUpdate(comm.TimeStat timeStat)
        {
        }
    }
    //#endregion

    //#region PlayerManager
    public sealed class PlayerManager
    {
        public static byte[] NewTcpMessage(comm.objst.TcpMessageType type, object obj, string msgId = null, long timeOffet = 0)
        {
            var msg = new comm.objst.TcpMessage
            {
                Type = type,
                Obj = obj,
                SendTime = DateTime.Now.Ticks,
                TimeOffset = timeOffet,
                MsgId = msgId != null ? msgId : ""
            };
            return comm.BinarySerializer.Serialize(msg);
        }

        public int AllOnlineCount { get { return _playerDic.Count; } }
        public int FPS { get { return _timeStat.FPS; } }

        comm.Timing _saveTiming = new comm.Timing();
        comm.TimeStat _timeStat = new comm.TimeStat();
        Dictionary<string, Player> _playerDic = new Dictionary<string, Player>();
        Dictionary<string, Player> _clientDic = new Dictionary<string, Player>();//<ClientId,PlayerId

        const int THREAD_SLEEP = 1000 / 40;
        const int SAVE_INTERVAL = 1000 * 10;

        TcpMessageFuncDic _tcpMessageDic = new TcpMessageFuncDic();

        World _world = null;
        comm.TcpServer _tcpServer = null;

        public PlayerManager(World world)
        {
            _world = world;

            StartListen();

            #region loop update
            comm.ThreadHelper.Run(() =>
            {
                while (true)
                {
                    _timeStat.Update();
                    var beginSave = _saveTiming.GetElapsedTime(_timeStat, SAVE_INTERVAL) > 0;
                    lock (_playerDic)
                    {
                        if (beginSave)
                        {
                            var begin = DateTime.Now;
                            var count = 0;
                            foreach (var player in _playerDic.Values)
                            {
                                count++;
                                player.Save();
                            }
                            if (count > 0)
                            {
                                Console.WriteLine("save(" + count + ") ms:" + (DateTime.Now - begin).TotalMilliseconds);
                            }
                        }
                        foreach (var player in _playerDic.Values)
                        {
                            player.Update(_timeStat);
                        }
                    }
                    Thread.Sleep(THREAD_SLEEP);
                }
            });
            #endregion
        }

        #region StartListen
        void StartListen()
        {
            var type = GetType();
            var enumType = typeof(comm.objst.TcpMessageType);
            var fields = enumType.GetFields();
            foreach (var fd in fields)
            {
                if (fd.Name.IndexOf("ToServer") == 0)
                {
                    var md = type.GetMethod(fd.Name, BindingFlags.NonPublic | BindingFlags.Instance);
                    if (md != null)
                    {
                        var mtype = (comm.objst.TcpMessageType)Enum.Parse(enumType, md.Name);
                        _tcpMessageDic.Add(mtype, (client, message) =>
                        {
                            md.Invoke(this, new object[] { client, message });
                        });
                    }
                    else
                    {
                        throw new Exception("StartListen " + fd.Name + " not found");
                    }
                }
            }

            _tcpServer = new comm.TcpServer(Conf.TCP_SERVER_IP, Conf.TCP_SERVER_PORT);
            _tcpServer.OnConnectCallback = ClientConnect;
            Console.WriteLine("TcpServer start " + Conf.TCP_SERVER_IP + ":" + Conf.TCP_SERVER_PORT);
        }
        void ClientConnect(comm.TcpClient client)
        {
            Console.WriteLine("TcpClient(" + _tcpServer.AllClientCount + ") Connect " + client.IP + ":" + client.Port + " " + client.ClientId);
            client.OnReceiveCallback = ClientReceive;
            client.OnCloseCallback = ClientClose;
        }
        void ClientReceive(comm.TcpClient client, byte[] data)
        {
            //try
            //{
            var message = comm.BinarySerializer.Deserialize<comm.objst.TcpMessage>(data);
            if (_tcpMessageDic.ContainsKey(message.Type))
            {
                //try
                //{
                _tcpMessageDic[message.Type](client, message);
                //}
                //catch (Exception ex)
                //{
                //    Console.WriteLine("PlayerManager ClientReceive() type:" + message.Type + " " + ex.Message + " \r\n" + ex.StackTrace);
                //}
            }
            //}
            //catch (Exception ex)
            //{
            //    Console.WriteLine("PlayerManager ClientReceive() " + ex.Message + " \r\n" + ex.StackTrace);
            //}
        }
        void ClientClose(comm.TcpClient client)
        {
            Console.WriteLine("TcpClient(" + _tcpServer.AllClientCount + ") Close " + client.IP + ":" + client.Port + " " + client.ClientId);
            Player player = null;
            _clientDic.TryGetValue(client.ClientId, out player);
            if (player != null)
            {
                LeaveWorld(player.PlayerId);
            }
        }
        void ClientSend(comm.TcpClient client, comm.objst.TcpMessageType type, object obj, string msgId = null, long timeOffset = 0)
        {
            ClientSend(client, NewTcpMessage(type, obj, msgId, timeOffset));
        }
        void ClientSend(comm.TcpClient client, byte[] data)
        {
            client.SendAsync(data);
        }
        #endregion

        //#region ToServer
        void ToServerGetServerTime(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, new long[] { DateTime.Now.Ticks });
        }
        void ToServerGetHeroTypeList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            var list = new List<string>();
            var all = _world.DefineFile.GetAllHero();
            foreach (var heroId in all)
            {
                if (_world.DefineFile.GetHero(heroId).CanCreateByPlayer)
                {
                    list.Add(heroId);
                }
            }
            ResponseClient(client, message, list.ToArray());
        }
        void ToServerGetStarList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, _world.StarManager.GetAllStarInfo());
        }
        void ToServerGetStarFactionList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<string>(starId =>
            {
                var star = _world.StarManager.GetStar(starId);
                ResponseClient(client, message, star.GetAllFactionInfo());
            });
        }
        void ToServerGetAllFactionDic(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, _world.StarManager.GetAllFactionInfo());
        }
        void ToServerGetMyHeroList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<string>(userId =>
            {
                var list = new List<comm.objst.HeroCreateInfo>();
                var all = _world.HeroManager.GetAllHero(userId);
                foreach (var item in all)
                {
                    list.Add(new comm.objst.HeroCreateInfo
                    {
                        UserId = item.UserId,
                        PlayerId = item.PlayerId,
                        DObjectId = item.DObjectId,
                        Nickname = item.Nickname,
                        IsOnline = _world.PlayerManager.PlayerIsOnline(item.PlayerId)
                    });
                }
                ResponseClient(client, message, list.ToArray());
            });
        }
        void ToServerCreateHero(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<comm.objst.CreateHero>(createHero =>
            {
                var rt = _world.HeroManager.CreateHero(createHero);
                ResponseClient(client, message, new comm.objst.MessageResult
                {
                    IsSucc = rt == comm.CodeStr.OK,
                    Message = rt,
                });
            });
        }

        void ToServerGetHeroInfo(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<string>(playerId =>
            {
                var player = GetPlayer(playerId);
                ResponseClient(client, message, player.GetHeroInfo());
            });
        }
        void ToServerGetMyHeroInfo(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, GetPlayerByClient(client).GetHeroFullInfo());
        }
        void ToServerGetMyHeroEquipList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, GetPlayerByClient(client).GetAllEquipInfo());
        }
        void ToServerGetMyBackpackList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, GetPlayerByClient(client).GetAllBackpackInfo());
        }
        void ToServerGetMyStateList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, GetPlayerByClient(client).HeroObject.StateManager.GetAllStateInfo());
        }

        void ToServerGetMyUnitList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            var player = GetPlayerByClient(client);
            ResponseClient(client, message, _world.UnitManager.GetAllUnitInfo(player.PlayerId));
        }
        void ToServerGetMyUnitReadyList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            ResponseClient(client, message, GetPlayerByClient(client).GetAllUnitReadyInfo());
        }
        void ToServerGetUnitEquipList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<string>(unitId =>
            {
                ResponseClient(client, message, _world.UnitManager.GetUnitAllEquipInfo(unitId));
            });
        }

        void ToServerGetStarSpaceList(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<string[]>(arr =>
            {
                var starId = arr[0];
                var areaId = arr[1];
                var star = _world.StarManager.GetStar(starId);
                ResponseClient(client, message, star.GetAllSpaceInfo(areaId));
            });
        }

        void ToServerHeroJoinWorld(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<string>(playerId =>
            {
                var rt = JoinWorld(playerId, client.ClientId);
                ResponseClient(client, message, new comm.objst.MessageResult
                {
                    IsSucc = rt == comm.CodeStr.OK,
                    Message = rt
                });
            });
        }
        void ToServerClientReady(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            var rt = "";
            var player = GetPlayerByClient(client);
            if (player == null)
            {
                rt = "clientId:" + client.ClientId + " not in world";
            }
            else
            {
                if (player.HeroObject != null)
                {
                    var list = new List<string>();
                    list.Add(player.ObjectId);
                    //for (int i = 0; i < 10; i++)
                    //{
                    //    var objectId = player.NewObjectId();
                    //    list.Add(objectId);
                    //    _world.ObjectManager.CreateObject("UnitBlade", objectId);
                    //}

                    //var objectId2 = player.NewObjectId();
                    //list.Add(objectId2);
                    //_world.ObjectManager.CreateObject("Unit战斗机", objectId2);

                    _world.ObjectManager.ObjectsJoinSpace(list.ToArray(), player.HeroObject.AtSpaceId, player.HeroObject.LocX, player.HeroObject.LocY, player);
                }
            }
            ResponseClient(client, message, new comm.objst.MessageResult
            {
                Message = rt
            });
        }
        void ToServerSpaceObjectBeginMove(comm.TcpClient client, comm.objst.TcpMessage message)
        {
            message.TryToObject<comm.objst.MoveTo[]>(moveTos =>
            {
                var player = GetPlayerByClient(client);
                var space = _world.SpaceManager.GetSpaceByPlayerId(player.PlayerId);
                var result = space.PlayerBeginMoveObjects(player, moveTos, DateTime.Now.Ticks, message.TimeOffset);
                ResponseClient(client, message, result);
            });
        }
        //#endregion

        public void ResponseClient(comm.TcpClient client, comm.objst.TcpMessage message, object obj, long timerOffset = 0)
        {
            ClientSend(client, comm.objst.TcpMessageType.ToClientCallback, obj, message.MsgId, timerOffset);
        }
        public void SendToPlayer(string playerId, comm.objst.TcpMessageType type, object obj, string msgId = null, long timeOffset = 0)
        {
            var player = GetPlayer(playerId);
            if (player != null)
            {
                var client = _tcpServer.GetClient(player.ClientId);
                ClientSend(client, type, obj, msgId, timeOffset);
            }
        }
        public void SendToPlayer(string playerId, byte[] data)
        {
            var player = GetPlayer(playerId);
            if (player != null)
            {
                var client = _tcpServer.GetClient(player.ClientId);
                ClientSend(client, data);
            }
        }

        string JoinWorld(string playerId, string clientId)
        {
            var tablePlayer = _world.HeroManager.GetHero(playerId);
            if (tablePlayer != null)
            {
                lock (_playerDic)
                {
                    if (_clientDic.ContainsKey(clientId))
                    {
                        return "client is online";
                    }
                    if (PlayerIsOnline(playerId))
                    {
                        return "player is online";
                    }
                    var player = new Player(_world, clientId, tablePlayer);
                    var obj = _world.ObjectManager.CreateObject(tablePlayer.DObjectId, player.ObjectId);
                    if (obj == null)
                    {
                        return "ObjectManager.CreateObject(Hero, " + player.ObjectId + ") err";
                    }
                    obj.NValueManager.SetAllNVValue(_world.DefineFile.GetAllNVValue(comm.objst.NValueGroup.HERO));
                    foreach (var key in tablePlayer.NValueDic.Keys)
                    {
                        obj.NValueManager.SetNVValue(key, tablePlayer.NValueDic[key]);
                    }
                    foreach (var state in tablePlayer.StateList)
                    {
                        obj.StateManager.AddState(state.StateId, state.Dur, state.BeginTime);
                    }
                    _playerDic.Add(playerId, player);
                    _clientDic.Add(clientId, player);
                    return comm.CodeStr.OK;
                }
            }
            return "player_id \"" + playerId + "\" not found";
        }
        string LeaveWorld(string playerId)
        {
            lock (_playerDic)
            {
                var player = GetPlayer(playerId);
                if (player != null)
                {
                    SavePlayer(playerId);
                    _world.ObjectManager.RemoveObjectsByPlayerId(player.PlayerId);
                    _clientDic.Remove(player.ClientId);
                    _playerDic.Remove(playerId);
                    return comm.CodeStr.OK;
                }
            }
            return "player_id \"" + playerId + "\" not online";
        }

        public Player GetPlayer(string playerId)
        {
            Player player = null;
            _playerDic.TryGetValue(playerId, out player);
            return player;
        }
        public Player GetPlayerByObjectId(string objectId)
        {
            var playerId = Player.ObjectIdToPlayerId(objectId);
            if (playerId != null)
            {
                return GetPlayer(playerId);
            }
            return null;
        }
        public Player GetPlayerByClient(comm.TcpClient client)
        {
            Player player = null;
            _clientDic.TryGetValue(client.ClientId, out player);
            return player;
        }
        public bool PlayerIsOnline(string playerId)
        {
            return _playerDic.ContainsKey(playerId);
        }
        public void SavePlayer(string playerId)
        {
            var player = GetPlayer(playerId);
            if (player != null)
            {
                player.Save();
            }
        }
    }
    //#endregion
}